package file

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	. "gitee.com/tomatomeatman/golang-repository/bricks/model"
	Log "github.com/cihub/seelog"
)

type FileUtil struct{}

//读取文件
func (f FileUtil) ReadFromFile(path string) *MsgEmity {
	if !f.IsExist(path) {
		return MsgEmity{}.Err(1001, "文件不存在")
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return MsgEmity{}.Err(1002, "读取失败")
	}

	return MsgEmity{}.Success(string(data), "读取成功")
}

// 调用os.MkdirAll递归创建文件夹
func (f FileUtil) CreateFile(filePath string) (bool, error) {
	if !f.IsExist(filePath) {
		err := os.MkdirAll(filePath, os.ModePerm)
		return false, err
	}

	return true, nil
}

// 判断所给路径文件/文件夹是否存在(返回true是存在)
func (f FileUtil) IsExist(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}

	return true
}

// 保存文件内容
func (f FileUtil) Save(text string, path string) (bool, error) {
	// ok, err := f.CreateFile(path)
	// if err != nil {
	// 	return false, err
	// }

	// if !ok {
	// 	return false, err
	// }

	iEd := strings.LastIndex(path, "/")
	dir := path[:iEd]
	if "." != dir {
		os.MkdirAll(dir, os.ModePerm)
	}

	//使用 ioutil.WriteFile写文件，在写入文件之前，不需要判断文件是否存在，如果文件不存在，会自动创建文件，如果文件存在，则会覆盖原来的内容
	err := ioutil.WriteFile(path, []byte(text), 0666) // 保存到文件
	if err != nil {
		return false, err
	}

	return true, nil
}

//删除文件
func (f FileUtil) Del(path string) (bool, error) {
	err := os.Remove(path)
	if err == nil { // 删除成功
		return true, err
	}

	// 删除失败
	ok := f.IsExist(path)
	if ok {
		return true, nil
	}

	Log.Error("删除文件失败:", err)

	return false, err
}

//获取指定目录下的所有文件和目录
func (this FileUtil) GetFilesAndDirs(dirPth string) (files []string, dirs []string, err error) {
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, nil, err
	}

	PthSep := string(os.PathSeparator)
	//suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写

	for _, fi := range dir {
		if fi.IsDir() { // 目录, 递归遍历
			dirs = append(dirs, dirPth+PthSep+fi.Name())
			this.GetFilesAndDirs(dirPth + PthSep + fi.Name())
		} else {
			// 过滤指定格式
			//if strings.HasSuffix(fi.Name(), ".go") {
			files = append(files, dirPth+PthSep+fi.Name())
			//}
		}
	}

	return files, dirs, nil
}

//获取指定目录下的所有文件,包含子目录下的文件
func (this FileUtil) GetAllFiles(dirPth string) (files []string, err error) {
	var dirs []string
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}

	PthSep := string(os.PathSeparator)
	//suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写

	for _, fi := range dir {
		if fi.IsDir() { // 目录, 递归遍历
			dirs = append(dirs, dirPth+PthSep+fi.Name())
			this.GetAllFiles(dirPth + PthSep + fi.Name())
		} else {
			// 过滤指定格式
			//if strings.HasSuffix(fi.Name(), ".go") {
			files = append(files, dirPth+PthSep+fi.Name())
			//}
		}
	}

	// 读取子目录下文件
	for _, table := range dirs {
		temp, _ := this.GetAllFiles(table)
		for _, temp1 := range temp {
			files = append(files, temp1)
		}
	}

	return files, nil
}

/**
 * 文件信息信息
 * @author HuangXinBian
 */
type FileInfo struct {
	GsName    string      `json:"sName"`    //名称
	GsPath    string      `json:"sPath"`    //路径
	GiDir     int         `json:"iDir"`     //是否目录
	GiSize    int64       `json:"iSize"`    //大小
	GdTime    string      `json:"dTime"`    //修改时间
	Gchildren []*FileInfo `json:"children"` //子文件
}

/**
 * 取文件夹信息树
 */
func (this FileUtil) GetFilesTree(rootName string) *MsgEmity {
	rootName = strings.TrimSpace(rootName)
	root, err := os.Stat(rootName)
	if err != nil {
		return MsgEmity{}.Err(1001, "没有数据!")
	}

	fileInfo := &FileInfo{
		GsName: root.Name(),
		GsPath: this.changePath(rootName),
		GiSize: root.Size(),
		GdTime: root.ModTime().Format("2006-01-02 15:04:05"),
		GiDir:  1,
	}

	this.buildTree(fileInfo, rootName)
	this.printTree(fileInfo)

	return MsgEmity{}.Success(fileInfo, "构造成功!")
}

//递归遍历文件夹及其子文件夹，构建节点树
func (this FileUtil) buildTree(node *FileInfo, vPath string) {
	files, err := ioutil.ReadDir(vPath)
	if err != nil {
		Log.Error("构建节点树异常:", err)
		return
	}

	for _, file := range files {
		if ".keep" == file.Name() {
			continue
		}

		fileInfo := FileInfo{}
		fileInfo.GsName = file.Name()
		fileInfo.GiSize = file.Size()
		fileInfo.GdTime = file.ModTime().Format("2006-01-02 15:04:05")

		if !file.IsDir() {
			fileInfo.GsPath = this.changePath(vPath + "/" + fileInfo.GsName)
			fileInfo.GiSize = file.Size()
			fileInfo.GiDir = 0
			node.Gchildren = append(node.Gchildren, &fileInfo)
			continue
		}

		childPath := filepath.Join(vPath, file.Name())
		fileInfo.GsPath = this.changePath(childPath)
		fileInfo.GiDir = 1

		node.Gchildren = append(node.Gchildren, &fileInfo)
		this.buildTree(&fileInfo, childPath)
	}
}

//递归节点树
func (this FileUtil) printTree(node *FileInfo) {
	for _, child := range node.Gchildren {
		this.printTree(child)
	}
}

//格式化路径
func (this FileUtil) changePath(vPath string) string {
	vPath = strings.Replace(vPath, "\\", "/", -1)

	if !strings.HasPrefix(vPath, "./") {
		vPath = "./" + vPath
	}

	return vPath
}

// 判断所给路径是否为文件夹
func (this FileUtil) IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}

	return s.IsDir()
}

// 判断所给路径是否为文件
func (this FileUtil) IsFile(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}

	return !s.IsDir()
}

//删除文件(文件夹)
func (this FileUtil) DelFiles(dir string, delDir bool) error {
	d, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer d.Close()

	names, err := d.Readdirnames(-1)
	if err != nil {
		return err
	}

	for _, name := range names {
		err = os.RemoveAll(dir + "/" + name)
		if err != nil {
			Log.Error("删除失败:", err)
			return err
		}
	}

	if !delDir {
		return nil
	}

	d.Close()

	err = os.Remove(dir)
	if err != nil {
		Log.Error("删除失败:", err)
		return err
	}

	return nil
}
